在上一篇文章中,我們提到底層的「管路」指令會做出 blob、tree、commit 等物件,而這些正是認識 git 結構的核心觀念!
全稱為 binary large object,即「二進制大型物件」,為檔案中的內容,但不包含與內容無關的元資料(metadata)。
比如說一個寫著 “Hello, world!”
的 .txt
文字檔,blob 就只會有 Hello, world!
這些文字本身,而不會有檔案名稱、檔案建立日期、檔案大小等資訊;檔案內容與元資料兩者組成一「檔案(file)」。
每個 blob 物件都以「安全雜湊演算法 1(Secure Hash Algorithm 1, SHA-1 hash)」計算出的雜湊碼識別,值得留意的是,因為 blob 只包含檔案內容,哪怕檔案經重新命名、搬到別的資料夾等只影響元資料的操作,這個 blob 物件與其對應的雜湊碼都不會改變。
表示目錄(directory)的結構,成員可包含 blob 或其他的 tree,每棵 tree 也都有自己的雜湊碼供識別。
tree 物件示意圖,每個 tree 可指向一個 blob 或另一棵 tree
上述 tree 物件示意圖可理解為以下目錄結構:
指的是某個 tree 在特定時間點下的快照(snapshot),除了包含指向主 tree(即根目錄)的指標,亦包含作者(author)、上代(parent)、訊息(message)等該 commit 的元資料。
commit 物件代表某主 tree 在一特定時間點下的快照
當這個 commit 指向的主 tree 中,有某個 blob 發生改變,那與該 blob 有關的 tree 都會跟著改變。
例如若把 hello_world.txt
中的驚嘆號拿掉,改為 Hello, world
,則會發生以下連鎖反應:
hello_world.txt
的內容改變,產生新 blob 物件,雜湊碼由 af5626b...
改為 a5c1966...
。af5626b...
的 tree 改為指向 a5c1966...
,因此產生新的 tree 物件,雜湊碼由 57639e7...
改為 5ea002f...
。57639e7...
的 tree 改為指向 5ea002f...
,因此產生新的 tree 物件,雜湊碼由 ab7c3a8...
改為 e12e0ca...
。ab7c3a8...
的 commit 改為指向 e12e0ca...
,因此產生新的 commit 物件,雜湊碼由 c14a9d9...
改為 3a313e6...
。
將 Hello, world! 驚嘆號拿掉後,引起的連鎖反應產生新物件流程圖
至於未發生改變的 blob 物件如 f924bfa...
與 702d670...
則不會出現新的雜湊碼,在新的 commit 物件 3a313e6...
中,若包含 f924bfa...
與 702d670...
兩 blob 的資訊,都直接指向舊 commit e12e0ca...
中對應的 blob,舊 commit 即為新 commit 的上代。
因此,每個新 commit 中儲存的資訊僅包含「與上代 commit 不同者」,沒發生改變的 tree 或 blob 都直接指向舊 commit 裡面的就好。
將 hello_world.txt 中內容改變後,產生新的 commit 變化圖
而不管哪個物件,都會有一組識別碼,上文中提及是透過「安全雜湊演算法 1(Secure Hash Algorithm 1, SHA-1 hash)」算出來的,所以這演算法到底是什麼?
現在我們來做個實驗,請依序輸入以下指令:
touch empty1.txt
(建立一名為 empty1.txt
的空文字檔)touch empty2.txt
(建立一名為 empty2.txt
的空文字檔)兩個文字檔名字不同,但內容都一樣空空如也,那這兩個檔案形成的雜湊碼分別是什麼呢?
讓我們分別輸入以下兩指令,檢視兩者產生的雜湊碼:
git hash-object empty1.txt
(查看 empty1.txt
文字檔產生的雜湊碼)git hash-object empty2.txt
(查看 empty2.txt
文字檔產生的雜湊碼)會得到什麼呢?
哇!兩個檔名不同、內容相同的檔案,雜湊碼都是 e69de29...
!怎麼會這樣?
在下一篇文章中,我們將探討雜湊碼的計算過程,以及其當中暗藏的玄機。
當我們輸入上層的「瓷器」指令如 git add
、git commit
,其實 git 都默默地幫我們產生 blob、tree 或 commit 物件,並建立好物件之間的關係,這結構便是我們透過 git 進行版本控制的基礎。